﻿using ClusteringProblem.Algorithms;
using ClusteringProblem.Algorithms.AntMethod;
using ClusteringProblem.Algorithms.DensityMethod;
using ClusteringProblem.Algorithms.HierarchicalMethod;
using ClusteringProblem.Algorithms.TraditionalMethod;
using ClusteringProblem.DataImport;
using ClusteringProblem.Maths;
using ClusteringProblem.Maths.Metrics;
using ClusteringProblem.Maths.QualityMeasures;
using ClusteringProblem.Model;
using ClusteringProblem.Model.DatasetInformation;
using ClusteringProblem.View;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ClusteringProblem.Presenter
{
    public class MainWindowPresenter
    {
        #region Pola

        private IMainWindowView view;
        private NACRE nacre;
        private ModifiedNACRE modNacre;
                
        List<IAntType> ants;
        private DatasetInfo datasetInfo;

        private int iterationCount;
        private int groupsCount;
        private double sigma;        
        private double epsilon;        
        private int minPts;      

        private bool isNACRE;        
        private bool isDBSCAN;        
        private bool isKMeans;
        private bool isModifiedNACRE;

        private bool loadDatasetFlag;
        private List<IAntType> resultList;
        List<Tuple<int, List<IAntType>>> result;

        #endregion

        #region Konstruktor

        public MainWindowPresenter(IMainWindowView view)
        {
            this.view = view;
        }

        #endregion

        #region Właściwości

        public int IterationCount 
        {
            get
            {
                return iterationCount;
            }
            set 
            {
                iterationCount = value;
                UpdateView();
            }
        }

        public int GroupsCount
        {
            get 
            {
                return groupsCount;
            }
            set
            {
                groupsCount = value;
                UpdateView();
            }
        }

        public double Sigma
        {
            get 
            { 
                return sigma; 
            }
            set 
            { 
                sigma = value;
                UpdateView();
            }
        }

        public double Epsilon
        {
            get 
            { 
                return epsilon; 
            }
            set 
            { 
                epsilon = value;
                UpdateView();
            }
        }

        public int MinPts
        {
            get 
            { 
                return minPts; 
            }
            set 
            { 
                minPts = value;
                UpdateView();
            }
        }

        public bool IsNACRE
        {
            get 
            { 
                return isNACRE; 
            }
            set 
            { 
                isNACRE = value;
                UpdateView();
            }
        }

        public bool IsDBSCAN
        {
            get 
            { 
                return isDBSCAN; 
            }
            set 
            { 
                isDBSCAN = value;
                UpdateView();
            }
        }

        public bool IsKMeans
        {
            get 
            { 
                return isKMeans; 
            }
            set 
            { 
                isKMeans = value;
                UpdateView();
            }
        }

        public bool IsModifiedNACRE
        {
            get
            {
                return isModifiedNACRE;
            }
            set
            {
                isModifiedNACRE = value;
                UpdateView();
            }
        }


        public bool LoadDatasetFlag
        {
            get
            { 
                return loadDatasetFlag; 
            }
            set
            {
                loadDatasetFlag = value;
                UpdateView();
            }
        }
        #endregion

        #region Metody

        /// <summary>
        /// Aktualizacja widoku
        /// </summary>
        private void UpdateView()
        {
            if (AllParametersIsFilled())
            {
                view.StartEnabled = true;
            }
            else
            {
                view.StartEnabled = false;
            }
        }

        /// <summary>
        /// Walidacja parametrów algorytmu
        /// </summary>
        /// <returns></returns>
        private bool AllParametersIsFilled()
        {
            bool flag = true;

            if (loadDatasetFlag)
            {
                flag = true;
            }
            else
            {
                flag = false;
            }

            if (isNACRE)
            {
                flag &= iterationCount > 0;
                flag &= sigma > 0;
            }
            else if (isModifiedNACRE)
            {
                flag &= iterationCount > 0;
            }
            else if (isDBSCAN)
            {
                flag &= epsilon > 0.0;
                flag &= minPts >= 0;
            }
            else if (isKMeans)
            {
                flag &= iterationCount > 0;
                flag &= groupsCount > 0;
            }
                       
            
            return flag;            
        }

        /// <summary>
        /// Ustawienie informacji o wybranym zbiorze danych
        /// </summary>
        public void SetDatasetInfo()
        {
            // fabryka - tworzenie info o zbiorze danych
            IDatasetInfoFactory datasetFactory = new DatasetInfoFactory();
            DatasetTypes datasetType = (DatasetTypes)Enum.Parse(typeof(DatasetTypes), view.DatasetName);
            datasetInfo = datasetFactory.CreateDatasetInfo(datasetType);

            datasetInfo.SetDataImage(view.DatasetName);

            view.Image = datasetInfo.Image;
            view.GroupsCount = datasetInfo.GroupsCount;
            view.ObjectsCount = datasetInfo.ObjectsCount;
            view.AttributesCount = datasetInfo.AttributesCount;
            view.Description = datasetInfo.Description;            
            
        }

        /// <summary>
        /// Odpala algorytm grupowania
        /// </summary>
        public void RunAlgorithm()
        {            
            ReadedDataset rd = new ReadedDataset();
            rd.SetReadedDataset((DatasetTypes)Enum.Parse(typeof(DatasetTypes), view.DatasetName));
            
            var objects = rd.ReadData(view.DatasetPath, view.IsNormalizeData);
            view.DataSource = objects;

            var antObjects = objects.Select(x => new AntType(x)).ToList();   
            ants = antObjects.Select(x => (IAntType)x).ToList<IAntType>();            

            // fabryka - wybór miary odległości
            IDistanceMetricsFactory distanceFactory = new DistanceMetricsFactory();
            DistanceMetricsTypes distanceType = (DistanceMetricsTypes)Enum.Parse(typeof(DistanceMetricsTypes), view.Distance);
            IDistance distance = distanceFactory.CreateMetric(distanceType);

            ComputedMeasure computedMeasure = new ComputedMeasure();

            if (isNACRE)
            {
                IClusteringAlgorithm alg = new HAC(Convert.ToInt32(datasetInfo.GroupsCount));
                Plane plane = new Plane(objects.Count());

                nacre = new NACRE(plane, distance, alg, iterationCount, sigma, ants);
                nacre.RunAlgorithm();

                result = new List<Tuple<int, List<IAntType>>>();
                result = nacre.Result;

                resultList = new List<IAntType>();                
                foreach (var tuple in result)
                    resultList.AddRange(tuple.Item2);

                view.DataResult = resultList.OrderBy(o => Convert.ToInt32(o.ObjectID)).ToList();                
                
                computedMeasure.SetQualityMeasureStrategy(new RandCoefficient(), resultList, objects);
                view.RandCoefficientValue = computedMeasure.Compute();

                computedMeasure.SetQualityMeasureStrategy(new FMeasure(), resultList, objects);
                view.FMeasureValue = computedMeasure.Compute();

                computedMeasure.SetQualityMeasureStrategy(new ClassificationError(), resultList, objects);
                view.ClassificationErrorValue = computedMeasure.Compute(); 

            }
            else if (isModifiedNACRE)
            {
                IClusteringAlgorithm alg = new HAC(Convert.ToInt32(datasetInfo.GroupsCount));
                Plane plane = new Plane(objects.Count());

                modNacre = new ModifiedNACRE(plane, distance, alg, iterationCount, ants);
                view.IsSigmaChartVisible = true;
                modNacre.OnSigmaValueChanged += modNacre_OnSigmaValueChanged;
                modNacre.RunAlgorithm(view.CancellationTokenSource);

                result = new List<Tuple<int, List<IAntType>>>();
                result = modNacre.Result;

                resultList = new List<IAntType>();
                foreach (var tuple in result)
                    resultList.AddRange(tuple.Item2);

                view.DataResult = resultList.OrderBy(o => Convert.ToInt32(o.ObjectID)).ToList();

                computedMeasure.SetQualityMeasureStrategy(new RandCoefficient(), resultList, objects);
                view.RandCoefficientValue = computedMeasure.Compute();

                computedMeasure.SetQualityMeasureStrategy(new FMeasure(), resultList, objects);
                view.FMeasureValue = computedMeasure.Compute();

                computedMeasure.SetQualityMeasureStrategy(new ClassificationError(), resultList, objects);
                view.ClassificationErrorValue = computedMeasure.Compute();
            }
            else if (isDBSCAN)
            {
                IClusteringAlgorithm dbscan = new DBSCAN(epsilon, minPts, distance);
                dbscan.RunAlgorithm(ants);

                result = new List<Tuple<int, List<IAntType>>>();
                result = dbscan.Result;

                resultList = new List<IAntType>();
                foreach (var tuple in result)
                    resultList.AddRange(tuple.Item2);

                view.DataResult = resultList.OrderBy(o => Convert.ToInt32(o.ObjectID)).ToList();

                computedMeasure.SetQualityMeasureStrategy(new RandCoefficient(), resultList, objects);
                view.RandCoefficientValue = computedMeasure.Compute();

                computedMeasure.SetQualityMeasureStrategy(new FMeasure(), resultList, objects);
                view.FMeasureValue = computedMeasure.Compute();

                computedMeasure.SetQualityMeasureStrategy(new ClassificationError(), resultList, objects);
                view.ClassificationErrorValue = computedMeasure.Compute(); 
            }
            else
            {
                IClusteringAlgorithm kmeans = new KMeans(iterationCount, groupsCount, distance);
                kmeans.RunAlgorithm(ants);

                result = new List<Tuple<int, List<IAntType>>>();
                result = kmeans.Result;

                resultList = new List<IAntType>();
                foreach (var tuple in result)
                    resultList.AddRange(tuple.Item2);

                view.DataResult = resultList.OrderBy(o => Convert.ToInt32(o.ObjectID)).ToList();
                
                computedMeasure.SetQualityMeasureStrategy(new RandCoefficient(), resultList, objects);
                view.RandCoefficientValue = computedMeasure.Compute();

                computedMeasure.SetQualityMeasureStrategy(new FMeasure(), resultList, objects);
                view.FMeasureValue = computedMeasure.Compute();

                computedMeasure.SetQualityMeasureStrategy(new ClassificationError(), resultList, objects);
                view.ClassificationErrorValue = computedMeasure.Compute(); 
            }
        }

        private void modNacre_OnSigmaValueChanged(double sigma)
        {
            view.Sigma = sigma;
        }               

        

        #endregion

    }
}
